#include "sdk.h"
#include <errno.h>
#include "projectfileoptionsdlg.h"
#include <wx/slider.h>
#include <wx/notebook.h>
#include <wx/textfile.h>

BEGIN_EVENT_TABLE( ProjectFileOptionsDlg, wxScrollingDialog )
  EVT_CHECKBOX( XRCID( "chkReadOnly" ), ProjectFileOptionsDlg::OnReadOnlyCheck )
  EVT_CHOICE( XRCID( "cmbBuildStageCompiler" ), ProjectFileOptionsDlg::OnCompilerCombo )
  EVT_UPDATE_UI( -1, ProjectFileOptionsDlg::OnUpdateUI )
END_EVENT_TABLE()

inline void AnalyseLine( const CommentToken &language, wxString line, bool &comment, bool &code, bool &multi_line_comment ) {
  int first_single_line_comment, first_multi_line_comment_begin, first_multi_line_comment_end;
  line = line.Trim( true );
  line = line.Trim( false );
  if( line.IsEmpty() ) {
    return;
  }
  if( language.lineComment.Length() > 0 ) {
    first_single_line_comment = line.Find( language.lineComment );
  } else {
    first_single_line_comment = -1;
  }
  if( language.streamCommentStart.Length() > 0 ) {
    first_multi_line_comment_begin = line.Find( language.streamCommentStart );
  } else {
    first_multi_line_comment_begin = -1;
  }
  if( language.streamCommentEnd.Length() > 0 ) {
    first_multi_line_comment_end = line.Find( language.streamCommentEnd );
  } else {
    first_multi_line_comment_end = -1;
  }
  if( multi_line_comment ) {
    comment = true;
    if( first_multi_line_comment_end > -1 ) {
      multi_line_comment = false;
      if( first_multi_line_comment_end + language.streamCommentEnd.Length() < line.Length() ) {
        AnalyseLine( language, line.Mid( first_multi_line_comment_end + language.streamCommentEnd.Length() ), comment, code, multi_line_comment );
      }
    }
  } else if( !multi_line_comment ) {
    if( ( first_single_line_comment > -1 ) && ( ( first_multi_line_comment_begin == -1 ) ||
        ( ( first_multi_line_comment_begin > -1 ) && ( first_single_line_comment < first_multi_line_comment_begin ) ) ) ) {
      comment = true;
      if( first_single_line_comment > 0 ) {
        code = true;
      }
    } else if( first_multi_line_comment_begin > -1 ) {
      multi_line_comment = true;
      comment = true;
      if( first_multi_line_comment_begin > 0 ) {
        code = true;
      }
      if( first_multi_line_comment_begin + language.streamCommentStart.Length() < line.Length() ) {
        AnalyseLine( language, line.Mid( first_multi_line_comment_begin + language.streamCommentStart.Length() ), comment, code, multi_line_comment );
      }
    } else {
      code = true;
    }
  }
}

inline void CountLines( wxFileName filename, const CommentToken &language, long int &code_lines, long int &codecomments_lines,
                        long int &comment_lines, long int &empty_lines, long int &total_lines ) {
  wxTextFile file;
  if( file.Open( filename.GetFullPath(), wxConvFile ) ) {
    bool multi_line_comment = false;
    total_lines += file.GetLineCount();
    for( unsigned int i = 0; i < file.GetLineCount(); ++i ) {
      wxString line = file[i];
      line = line.Trim( true );
      line = line.Trim( false );
      bool comment = false;
      bool code = false;
      if( line.IsEmpty() ) {
        ++empty_lines;
      } else {
        AnalyseLine( language, line, comment, code, multi_line_comment );
        if( comment && code ) {
          ++codecomments_lines;
        } else if( comment ) {
          ++comment_lines;
        } else if( code ) {
          ++code_lines;
        }
      }
    }
  }
}

ProjectFileOptionsDlg::ProjectFileOptionsDlg( wxWindow * parent, const wxString & fileName, ProjectFile* pf ) : m_ProjectFile( pf ), m_FileNameStr( fileName ), m_FileName(), m_LastBuildStageCompilerSel( -1 ) {
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/文件属性.xrc" );
  if( !xml_res->Load( resPath ) )  {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    return;
  }
  if( !xml_res->LoadObject( this, parent, _T( "dlgProjectFileOptions" ), _T( "wxScrollingDialog" ) ) ) {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( F( _T( "XRC错误: %s " ), resPath.wx_str() ) );
    return;
  }
  xml_File_lock = 1;
  XRCCTRL( *this, "wxID_OK", wxButton )->SetDefault();
  if( !m_FileNameStr.IsEmpty() ) {
    FillGeneralProperties();
    XRCCTRL( *this, "txtProject", wxTextCtrl )->SetValue( m_FileNameStr );
    XRCCTRL( *this, "lblAutoGen", wxStaticText )->Show( false );
    SetTitle( _T( "属性 " ) + wxString( _T( "\"" ) ) + m_FileNameStr + wxString( _T( "\"" ) ) );
  } else if( pf ) {
    M_Code_Project* prj = pf->GetParentProject();
    wxCheckListBox *list = XRCCTRL( *this, "lstTargets", wxCheckListBox );
    for( int i = 0; i < prj->GetBuildTargetsCount(); ++i ) {
      wxString targetName = prj->GetBuildTarget( i )->GetTitle();
      list->Append( targetName );
      if( pf->buildTargets.Index( targetName ) != -1 ) {
        list->Check( i, true );
      }
    }
    m_FileNameStr = pf->file.GetFullPath();
    FillGeneralProperties();
    XRCCTRL( *this, "txtCompiler", wxTextCtrl )->SetValue( pf->compilerVar );
    XRCCTRL( *this, "chkCompile", wxCheckBox )->SetValue( pf->compile );
    XRCCTRL( *this, "chkLink", wxCheckBox )->SetValue( pf->link );
    XRCCTRL( *this, "sliderWeight", wxSlider )->SetValue( pf->weight );
    XRCCTRL( *this, "txtObjName", wxTextCtrl )->SetValue( pf->GetObjName() );
    FillCompilers();
    UpdateBuildCommand();
    XRCCTRL( *this, "txtProject", wxTextCtrl )->SetValue( prj ? ( prj->GetTitle() + _T( "\n" ) + prj->GetFilename() + _T( "\n" ) + m_FileNameStr ) : _T( "-" ) );
    SetTitle( _T( "属性 " ) + wxString( _T( "\"" ) ) + pf->relativeFilename + wxString( _T( "\"" ) ) );
    XRCCTRL( *this, "txtObjName", wxTextCtrl )->Enable( false );
    if( pf->AutoGeneratedBy() ) {
      XRCCTRL( *this, "tabBuild", wxPanel )->Enable( false );
      XRCCTRL( *this, "tabAdvanced", wxPanel )->Enable( false );
    }
    XRCCTRL( *this, "lblAutoGen", wxStaticText )->Show( pf->AutoGeneratedBy() );
  }
}

ProjectFileOptionsDlg::~ProjectFileOptionsDlg() {
}

void ProjectFileOptionsDlg::OnReadOnlyCheck( wxCommandEvent & event ) {
  if( m_FileNameStr.IsEmpty() || !m_FileName.FileExists() ) {
    return;
  }
  if( event.IsChecked() ) {
    if( !ToggleFileReadOnly( true ) ) {
      Manager::Get()->GetLogManager()->LogError( F( _T( "无法设置文件 '%s' 只读状态." ), m_FileNameStr.wx_str() ) );
    }
  } else {
    if( !ToggleFileReadOnly( false ) ) {
      Manager::Get()->GetLogManager()->LogError( F( _T( "无法设置文件 '%s' 无权限." ), m_FileNameStr.wx_str() ) );
    }
  }
  XRCCTRL( *this, "chkReadOnly", wxCheckBox )->SetValue( !m_FileName.IsFileWritable() );
  Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
}
void ProjectFileOptionsDlg::OnCompilerCombo( wxCommandEvent & event ) {
  if( m_LastBuildStageCompilerSel != event.GetSelection() ) {
    SaveBuildCommandSelection();
    m_LastBuildStageCompilerSel = event.GetSelection();
    UpdateBuildCommand();
  }
}
void ProjectFileOptionsDlg::OnUpdateUI( M_Code_unused wxUpdateUIEvent & event ) {
  if( xml_File_lock ) {
    if( m_ProjectFile ) {
      bool en = XRCCTRL( *this, "chkBuildStage", wxCheckBox )->GetValue();
      XRCCTRL( *this, "txtBuildStage", wxTextCtrl )->Enable( en );
    } else {
      XRCCTRL( *this, "txtCompiler", wxTextCtrl )->Enable( false );
      XRCCTRL( *this, "lstTargets", wxCheckListBox )->Enable( false );
      XRCCTRL( *this, "chkCompile", wxCheckBox )->Enable( false );
      XRCCTRL( *this, "chkLink", wxCheckBox )->Enable( false );
      XRCCTRL( *this, "txtObjName", wxTextCtrl )->Enable( false );
      XRCCTRL( *this, "chkBuildStage", wxCheckBox )->Enable( false );
      XRCCTRL( *this, "txtBuildStage", wxTextCtrl )->Enable( false );
      XRCCTRL( *this, "sliderWeight", wxSlider )->Enable( false );
    }
  }
}
void ProjectFileOptionsDlg::EndModal( int retCode ) {
  if( retCode == wxID_OK && m_ProjectFile ) {
    wxCheckListBox *list = XRCCTRL( *this, "lstTargets", wxCheckListBox );
    for( size_t i = 0; i < list->GetCount(); i++ ) {
      if( list->IsChecked( i ) ) {
        m_ProjectFile->AddBuildTarget( list->GetString( i ) );
      } else {
        m_ProjectFile->RemoveBuildTarget( list->GetString( i ) );
      }
    }
    m_ProjectFile->compile = XRCCTRL( *this, "chkCompile", wxCheckBox )->GetValue();
    m_ProjectFile->link = XRCCTRL( *this, "chkLink", wxCheckBox )->GetValue();
    m_ProjectFile->weight = XRCCTRL( *this, "sliderWeight", wxSlider )->GetValue();
    SaveBuildCommandSelection();
    m_ProjectFile->compilerVar = XRCCTRL( *this, "txtCompiler", wxTextCtrl )->GetValue();
    if( m_ProjectFile->compile && m_ProjectFile->compilerVar.IsEmpty() ) {
      m_ProjectFile->compilerVar = _T( "CPP" );
    }
    M_Code_Project* prj = m_ProjectFile->GetParentProject();
    prj->SetModified( true );
    Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
  }
  wxScrollingDialog::EndModal( retCode );
}
void ProjectFileOptionsDlg::FillGeneralProperties() {
  m_FileName.Assign( m_FileNameStr );
  if( !m_FileName.FileExists() ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  wxFile file( m_FileName.GetFullPath() );
  if( file.IsOpened() ) {
    const HighlightLanguage& lang = colour_set->GetLanguageForFilename( m_FileNameStr );
    if( lang != HL_NONE ) {
      long int total_lines = 0;
      long int code_lines = 0;
      long int empty_lines = 0;
      long int comment_lines = 0;
      long int codecomments_lines = 0;
      CountLines( m_FileName, colour_set->GetCommentToken( lang ), code_lines, codecomments_lines, comment_lines, empty_lines, total_lines );
      XRCCTRL( *this, "staticTotalLines",   wxStaticText )->SetLabel( F( _T( "行总数: %ld" ), total_lines ) );
      XRCCTRL( *this, "staticEmptyLines",   wxStaticText )->SetLabel( F( _T( "空行数: %ld" ), empty_lines ) );
      XRCCTRL( *this, "staticActualLines",  wxStaticText )->SetLabel( F( _T( "实际代码行数: %ld" ), code_lines + codecomments_lines ) );
      XRCCTRL( *this, "staticCommentLines", wxStaticText )->SetLabel( F( _T( "注释行数: %ld" ), comment_lines ) );
      XRCCTRL( *this, "staticEmptyLines",   wxStaticText )->GetContainingSizer()->Layout();
    }
    long length = static_cast<long>( file.Length() );
    XRCCTRL( *this, "staticFileSize", wxStaticText )->SetLabel( F( _T( "文件大小 : %ld Bytes" ), length ) );
    XRCCTRL( *this, "staticFileSize", wxStaticText )->GetContainingSizer()->Layout();
    XRCCTRL( *this, "chkReadOnly", wxCheckBox )->SetValue( !m_FileName.IsFileWritable() );
    wxDateTime modTime = m_FileName.GetModificationTime();
    XRCCTRL( *this, "staticDateTimeStamp", wxStaticText )->SetLabel( F( _T( "文件时间戳: %02hd/%02hd/%d %02hd:%02hd:%02hd" ), modTime.GetDay(),
        modTime.GetMonth() + 1, modTime.GetYear(), modTime.GetHour(), modTime.GetMinute(), modTime.GetSecond() ) );
    file.Close();
    return;
  }
  XRCCTRL( *this, "chkReadOnly", wxCheckBox )->Enable( false );
  XRCCTRL( *this, "txtProject", wxTextCtrl )->Enable( false );
}
void ProjectFileOptionsDlg::FillCompilers() {
  wxChoice* cmb = XRCCTRL( *this, "cmbBuildStageCompiler", wxChoice );
  cmb->Clear();
  for( unsigned int i = 0; i < CompilerFactory::GetCompilersCount(); ++i ) {
    Compiler* compiler = CompilerFactory::GetCompiler( i );
    if( compiler ) {
      cmb->Append( compiler->GetName() );
    }
  }
  m_LastBuildStageCompilerSel = CompilerFactory::GetCompilerIndex( m_ProjectFile->GetParentProject()->GetCompilerID() );
  cmb->SetSelection( m_LastBuildStageCompilerSel );
}
void ProjectFileOptionsDlg::UpdateBuildCommand() {
  wxChoice* cmb = XRCCTRL( *this, "cmbBuildStageCompiler", wxChoice );
  int idx = cmb->GetSelection();
  Compiler* compiler = CompilerFactory::GetCompiler( idx );
  if( !compiler ) {
    return;
  }
  FileType ft = FileTypeOf( m_ProjectFile->relativeFilename );
  wxString cmd;
  if( ft == ftResource ) {
    cmd = compiler->GetCommand( ctCompileResourceCmd );
  } else if( ft == ftSource || ft == ftHeader ) {
    cmd = compiler->GetCommand( ctCompileObjectCmd );
  }
  XRCCTRL( *this, "lblBuildCommand", wxStaticText )->SetLabel( _T( "Default: " ) + cmd );
  Layout();
  XRCCTRL( *this, "chkBuildStage", wxCheckBox )->SetValue( m_ProjectFile->customBuild[compiler->GetID()].useCustomBuildCommand );
  XRCCTRL( *this, "txtBuildStage", wxTextCtrl )->SetValue( m_ProjectFile->customBuild[compiler->GetID()].buildCommand );
}
void ProjectFileOptionsDlg::SaveBuildCommandSelection() {
  Compiler* compiler = CompilerFactory::GetCompiler( m_LastBuildStageCompilerSel );
  if( compiler ) {
    m_ProjectFile->customBuild[compiler->GetID()].useCustomBuildCommand = XRCCTRL( *this, "chkBuildStage", wxCheckBox )->GetValue();
    m_ProjectFile->customBuild[compiler->GetID()].buildCommand = XRCCTRL( *this, "txtBuildStage", wxTextCtrl )->GetValue();
  }
}
bool ProjectFileOptionsDlg::ToggleFileReadOnly( bool setReadOnly ) {
  const int X_MODE_MASK = S_IRGRP | S_IROTH;
  int mask = setReadOnly ? S_IRUSR : ( S_IRUSR | S_IWUSR );
  int res  = chmod( m_FileNameStr.mb_str( wxConvUTF8 ), mask | X_MODE_MASK );
  if( res != 0 ) {
    Manager::Get()->GetLogManager()->DebugLog( F( _T( "Error calling chmod : errno = %d." ), errno ) );
    return false;
  }
  return ( ( setReadOnly && !m_FileName.IsFileWritable() ) || ( !setReadOnly &&  m_FileName.IsFileWritable() ) );
}
